package id.co.shuba.spamobile.task;

import id.co.shuba.spamobile.App;
import id.co.shuba.spamobile.Configs;
import id.co.shuba.spamobile.CustomSessionApplication;
import id.co.shuba.spamobile.Encryptor;
import id.co.shuba.spamobile.R;
import id.co.shuba.spamobile.Sessions;
import id.co.shuba.spamobile.WSException;
import id.co.shuba.spamobile.activities.DownloadActivity;
import id.co.shuba.spamobile.activities.MainActivity;
import id.co.shuba.spamobile.activities.UpdateActivity;
import id.co.shuba.spamobile.activities.UpdateMasterOfflineActivity;
import id.co.shuba.spamobile.activities.UpdateOfflineActivity;
import id.co.shuba.spamobile.data.DatabaseHandler;
import id.co.shuba.spamobile.data.ProspectDB;
import id.co.shuba.spamobile.data.UserDB;
import id.co.shuba.spamobile.model.UserInfo;
import id.co.shuba.spamobile.services.WebServiceHelper;
import id.co.shuba.spamobile.util.Base64;

import java.io.File;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParserException;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Debug;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.widget.Toast;

public class LoginTask extends
		AsyncTask<String, Integer, AsyncTaskResult<LoginStatus>> {		
		
	private static final int MAX_TRY = 3;
	private DatabaseHandler _db;
	private ProgressDialog _dialog;
	private Activity _ctx;		
	private CheckForUpdateTask _task;
	private SyncTableTask _syncTableTask;
	private UserInfo _user;	

	public LoginTask(DatabaseHandler db, ProgressDialog dialog, Activity ctx) {
		_db = db;
		_dialog = dialog;
		_ctx = ctx;
	}
	
	public CheckForUpdateTask getCheckForUpdateTask() {
		return _task;
	}
	
	public SyncTableTask getSyncTableTask() {
		return _syncTableTask;
	}
	
	public void setActivity(Activity ctx) {
		_ctx = ctx;
	}
	
	public void setDialog(ProgressDialog dialog) {			
		_dialog = dialog;
		if (getStatus() == Status.RUNNING)
			_dialog.show();
	}

	@Override
	protected AsyncTaskResult<LoginStatus> doInBackground(String... params) {
		try {			
			String nip = params[0];
			String pass = params[1];
			String imei = params[2];				

			try {				
				_user = wsLogin(nip, pass, imei);				
			} catch (WSException we) {
				Log.e("Login", "doInBackground.WSException: " + we.getMessage());				
				return new AsyncTaskResult<LoginStatus>(
						new LoginStatus(LoginStatus.STATUS_ERROR, params, we));
			} catch (Exception e) {
				Log.e("Login", "doInBackground.Exception", e);				
				if (e.getMessage() != null)
					Log.e("Login", e.getMessage());							
				
				return new AsyncTaskResult<LoginStatus>(
						new LoginStatus(LoginStatus.STATUS_RETRY, params, e));
			}							

			return new AsyncTaskResult<LoginStatus>(new LoginStatus(LoginStatus.STATUS_SUCCESS));
		} catch (Exception e) {
			Log.e("Login", "doInBackground.Exception2", e);			
			if (e.getMessage() != null)
				Log.e("Login", e.getMessage());
			return new AsyncTaskResult<LoginStatus>(e);
		}
	}

	@Override
	protected void onPreExecute() {
		super.onPreExecute();

		_dialog.show();
	}
	
	private void setLogin(String aoCode) throws Exception {
		Log.d("Login", "setLogin");
		WebServiceHelper ws = new WebServiceHelper(_ctx);		
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("aoCode", aoCode);
		ws.Call("SetLogin", params);		
	}

	@SuppressLint("SimpleDateFormat")
	private UserInfo wsLogin(String nip, String password, String imei) throws Exception {		
		WebServiceHelper ws = new WebServiceHelper(_ctx);		
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("nip", nip);
		params.put("password", password);
		params.put("imei", imei);

		int tryConnect = 1;		
					
		Object objResult = null;
		while (objResult == null && tryConnect <= MAX_TRY) {
			Log.d("Login", "wsLogin.Call --- try: "+ tryConnect);
			try {
				objResult = ws.Call("Login", params);				
			} catch (SocketTimeoutException ste) {				
				if (!ste.getMessage().contains("Read timed out"))
					throw ste;							
			} catch (Exception e) {
				throw e;
			}			
			tryConnect++;
		}
		
		if (objResult == null)
			throw new WSException("Failed to connect web service.");
		
				
		String result = objResult.toString();
		String strJSON = new String(Base64.decode(result, Base64.GZIP));

		JSONObject res = new JSONObject(strJSON);
		String error = res.getString("error");
		if (error.length() > 0 && !error.equals("null"))			
			throw new WSException(error);
		

		UserInfo info = new UserInfo();
		JSONObject obj = res.getJSONObject("result");
		info.setId(obj.getInt("id"));
		info.setAoCode(obj.getString("aoCode"));
		info.setUnitCode(obj.getString("unitCode"));
		info.setEmailAddress(obj.getString("emailAddress"));
		info.setUserName(obj.getString("userName"));
		info.setEncryptionKey(obj.getString("encryptionKey"));
		info.setNip(nip);
		info.setImei(imei);
		info.setToken(obj.getString("token"));		
		info.setSourceCode(obj.getString("sourceCode"));		
		info.setTimeOut(obj.getInt("timeOut"));
		info.setAoType(obj.getString("aoType"));
		
		Log.d("Login", "wsLogin.setConfigs");
		Configs.setConfigs(_ctx,
				obj.getInt("guCloseDeal"),
				obj.getInt("guFup"),
				obj.getInt("purgingPeriod"),
				obj.getString("processDate"));
		
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		App.processDate = sdf.parse(obj.getString("processDate"));
				
		info.setLastOnline(App.dateTime.parse(App.getProcessDateTime()));
		info.setLastLogin(App.dateTime.parse(App.getProcessDateTime()));
		
		Encryptor enc = new Encryptor(
				Encryptor.ENC_AES,
				Encryptor.ENC_AES, App.createCompleteKey(info.getEncryptionKey()), App.IV);
		Log.d("Login", "wsLogin.encryptPassword");
		info.setPassword(enc.encryptText(password));		
				
		UserDB uDB = new UserDB(_db);
		Log.d("Login", "wsLogin.updateUser");
		uDB.addOrUpdate(info);
						
		Log.d("Login", "wsLogin.setSessions");
		Sessions.setUser(_ctx, info);		
		Log.d("Login", "wsLogin.setBranchID");
		App.setBranchID(_ctx, info.getUnitCode());
		
		try {
			setLogin(info.getAoCode());
		} catch (Exception e) {			
			Log.e("Login", "setLogin failed", e);
			e.printStackTrace();
		}		
		
		return info;
	}	

	@Override
	protected void onPostExecute(AsyncTaskResult<LoginStatus> result) {
		super.onPostExecute(result);
				
		_dialog.dismiss();
						
		if (result.getError() != null) {					
			Toast.makeText(_ctx,
					result.getError().getMessage(), Toast.LENGTH_LONG).show();
			return;
		}
		
		final LoginStatus status = result.getResult();
		
		switch (status.getStatus()) {
		case LoginStatus.STATUS_RETRY:
			retry(status);
			break;
		case LoginStatus.STATUS_ERROR:
			Toast.makeText(_ctx,
					result.getResult().getError().getMessage(), Toast.LENGTH_LONG).show();
			break;
		case LoginStatus.STATUS_SUCCESS:	
			CustomSessionApplication app = (CustomSessionApplication)_ctx.getApplication();		
			app.setTimeInMinute(_user.getTimeOut());
			app.touch();
			
			syncTable();
			break;
		default:
			break;
		}					
	}	
	
	private void syncTable() {		
		Log.d("Login", "syncTable");
		ProgressDialog dialog = new ProgressDialog(_ctx);
		dialog.setCancelable(false);
		dialog.setMessage("Sync Table...");
		
		_syncTableTask = new SyncTableTask(_db, new WebServiceHelper(_ctx), dialog);
		_syncTableTask.setOnCheckingFinished(new OnDownloadCompleteListener() {			
			@Override
			public void onDownloadComplete(Exception ex) {
				if (ex != null)
					Log.e("Sync Table", ex.getMessage());
				
				purgeData();			
				rejectProspectBySystem(_user.getNip());
				checkForUpdate();				
			}
		});
		
		_syncTableTask.execute();
	}
	
	private void checkForUpdate() {			
		ProgressDialog dialog = new ProgressDialog(_ctx);
		dialog.setCancelable(false);
		dialog.setMessage("Check for Update...");
		
		_task = new CheckForUpdateTask(new WebServiceHelper(_ctx),
				Sessions.getUser(_ctx), App.getImei(_ctx), true, dialog);
		
		_task.setOnCheckingFinished(new OnCheckingFinished() {
			@Override
			public void updateNotFound() {
				goToDownload();
			};
			
			@Override
			public void updateFound(String version, String size, long checksum) {						
				SharedPreferences prefs = PreferenceManager
						.getDefaultSharedPreferences(_ctx);		
				String server = prefs.getString("ws_server", "spam.shuba.net");
				String path = prefs.getString("ws_path", "/");
				boolean isSecure = prefs.getBoolean("ws_secure", true);
				int port = Integer.parseInt(prefs.getString("ws_port", "443"));
				String portString = (isSecure? (port == 443? "" : ":" + String.valueOf(port)) : "");
									
				String url = String.format("%s://%s%s%sDownload.ashx?d=%s&v=%s",
						(isSecure? "https" : "http"), server, portString, path, App.getImei(_ctx), version);					
				
				Intent intent = new Intent(_ctx, UpdateActivity.class);
				intent.putExtra("URL", url);
				intent.putExtra("SIZE", size);
				intent.putExtra("VERSION", version);
				intent.putExtra("CHECKSUM", checksum);
				_ctx.startActivity(intent);
				_ctx.finish();					
			}
		});
		
		_task.execute();
	}
	
	private void retry(final LoginStatus status) {
		new AlertDialog.Builder(new ContextThemeWrapper(_ctx, R.style.Theme_Sherlock_Light_Dialog))
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle(R.string.error)
        .setMessage("Tidak dapat connect ke server")	        
        .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
            	dialog.dismiss();	            	
            }
        })
        .setNeutralButton(R.string.offline, new DialogInterface.OnClickListener() {				
			@Override
			public void onClick(DialogInterface dialog, int which) {
				dialog.dismiss();
				offline(status.getParams());
			}
		})
        .setPositiveButton(R.string.retry, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
            	dialog.dismiss();
            	LoginTask task = new LoginTask(_db, _dialog, _ctx);
    			task.execute(status.getParams());
            }
        })        
        .show();
	}
	
	private void goToDownload() {
		Intent intent = new Intent(_ctx, DownloadActivity.class);
		intent.putExtra(DownloadActivity.PARAMETER_OPTION,
				DownloadActivity.DOWNLOAD_MASTER);
		intent.putExtra(DownloadActivity.PARAMETER_AUTO_START, true);
		intent.putExtra(DownloadActivity.PARAMETER_EXIT_WHEN_DONE, true);
		intent.putExtra(DownloadActivity.PARAMETER_DOWNLOAD_UPDATE, true);
		
		File file = App.getOfflineUpdatePath("master.spapkg");
		File master = App.getOfflineUpdatePath("update.spapkg");
		if (file.exists() || master.exists()) {
			intent.putExtra(DownloadActivity.PARAMETER_GO_TO_DOWNLOAD_OFFLINE_WHEN_DONE, true);
		} else {
			intent.putExtra(DownloadActivity.PARAMETER_GO_TO_MAIN_WHEN_DONE, true);
		}		
		
		_ctx.startActivity(intent);
		_ctx.finish();
	}
	
	private void purgeData() {
		Log.d("Login", "purgeData");
		int purgePeriod = Configs.getPurgingPeriod(_ctx);
		Calendar cal = GregorianCalendar.getInstance(); 
		cal.setTime(App.processDate);
		cal.add(Calendar.DAY_OF_MONTH, purgePeriod);
		
		_db.purgeData(App.dateOnly.format(cal.getTime()));
	}
	
	private void rejectProspectBySystem(String user) {
		Log.d("Login", "rejectProspectBySystem");
		ProspectDB pDB = new ProspectDB(_db);
		pDB.rejectProspectBySystem(user, App.getProcessDateTime());
	}	
			
	@SuppressLint("NewApi")
	private void offline(String[] params) {
		Log.d("Login", "offline");
		String nip = params[0];
		String pass = params[1];		
		
		try {
    		
    		UserDB user = new UserDB(_db);
			UserInfo info = user.get(nip);			
			if (info == null)
				throw new Exception(String.format("User dengan nip %s tidak ditemukan", nip));
			
			Log.d("Login", "offline.getBranchID");
			String branchID = App.getBranchID(_ctx);
			if (branchID == null)
				throw new Exception("Branch ID null");
			
			Log.d("Login", "offline.decrypt");			
			Encryptor enc = new Encryptor(
					Encryptor.ENC_AES,
					Encryptor.ENC_AES, App.createCompleteKey(info.getEncryptionKey()), App.IV);
			
			if (!info.getPassword().equals(enc.encryptText(pass)))
				throw new Exception("Username atau password salah");
			
			if (!info.getUnitCode().equals(branchID))
				throw new Exception("User tidak berhak menggunakan device ini");
			
			
			
			long diff = TimeUnit.MILLISECONDS.toDays(new Date().getTime() - info.getLastOnline().getTime());			
			Log.d("Login", "offline.checkExpiredDate(" + diff + ", " + App.EXPIRED_OFFLINE_DAY+")");
			if (diff > App.EXPIRED_OFFLINE_DAY)
				throw new Exception(_ctx.getString(R.string.offline_login_expired));									
			
			
			Log.d("Login", "offline.setLastLogin");
			info.setLastLogin(user.updateLoginInfo(info.getId(), false));					
			
			
			Log.d("Login", "offline.setSessions");
			Sessions.setUser(_ctx, info);

			Intent intent = null;
			
			File file = App.getOfflineUpdatePath("update.spapkg");
			File master = App.getOfflineUpdatePath("master.spapkg");
			if (master.exists()) {
				Log.d("Login", "offline.foundMasterData");
				intent = new Intent(_ctx, UpdateMasterOfflineActivity.class);
				intent.putExtra("FILE", master.getPath());
				intent.putExtra("GOTO_MAIN", true);
			} else if (file.exists()) {
				Log.d("Login", "offline.foundUpdateData");
				intent = new Intent(_ctx, UpdateOfflineActivity.class);
				intent.putExtra("FILE", file.getPath());
				intent.putExtra("GOTO_MAIN", true);
			} else {
				intent = new Intent(_ctx, MainActivity.class);
			}
			
			purgeData();
			rejectProspectBySystem(info.getNip());
			
			_ctx.startActivity(intent);
			_ctx.finish();
			
		} catch (Exception e) {
			if (e.getMessage() != null) {
				Log.e("Login", e.getMessage());
				Toast.makeText(_ctx,
					e.getMessage(), Toast.LENGTH_LONG).show();
			}
		}			
	}			
}
